package Flap::Template::Document;
use Flap qw/-b Flap::Object -m -s -w/;
__PACKAGE__->_mk_accessors_(qw/location/);

sub block {
    my($self, $dtv, $block) = @_;
    my $my = $self->{my};
    @$block{qw/begin end my/} = ($dtv->{id}, $dtv->{next_id}, {map{ $_ => 1 } keys %$my});
    if(my $bl = $block->{local}){
        foreach(keys %$bl){
            if(exists $my->{$_}){
                ($my->{$_}, $bl->{$_}) = ($bl->{$_}, $my->{$_});
            }else{
                $my->{$_} = delete $bl->{$_};
            }
        }
    }
    push @{$self->{block}}, $block;
    $dtv->{id} + 1;
}

sub block_end {
    my $self = shift;
    my $my = $self->{my};
    my $block = pop @{$self->{block}};
    if(my $bl = $block->{local}){
        $my->{$_} = $bl->{$_} for keys %$bl;
    }
    $block->{my}{$_} || delete $my->{$_} for keys %$my;
}

sub clone {
    my $self = shift;
    (ref $self)->new({%$self});
}

sub init {
    my($self, $ft) = @_;
    $self->{$_} = $ft->{$_} for qw/our stash/;
    @$self{qw/block idx my tmp/} = ([], 0, {}, {});
    $self;
}

sub loop_block {
    my $self = shift;
    my $i = $#{$self->{block}};
    while($i >= 0){
        my $lb = $self->{block}[$i--];
        return $lb if $lb->{local} && $lb->{local}{loop};
    }
    undef;
}

sub new { bless $_[1], $_[0] }

sub path {
    my $loc = shift->{location} || return '?';
    $loc->[0].$loc->[1];
}

sub rel2abs {
    my $self = shift;
    my $r = ref $_[0] ? shift : do{ \(my $rel = shift) };
    my $src = $self->{location} && $self->{location}[1] || die qq{No base src for "$$r"};
    
    $$r =~ s%^\./%%;
    $src =~ s%/[^/]+\z%% || die;
    $src =~ s%/[^/]+\z%% || die while $$r =~ s%^\.\./%%;
    $$r = "$src/$$r";
}

sub var {
    my($self, $k) = (shift, shift);
    my $i = ($self->{strict} || $self->{warnings}) ? $k : undef;
    my $v = exists $self->{my}{$k} ? $self->{my}{$k} :
        exists $self->{our}{$k}    ? $self->{our}{$k} :
        exists $self->{stash}{$k}  ? $self->{stash}{$k} :
        $self->{strict} ? die qq{"$k" was not declared in this scope} :
        undef;
    return $self->_var_undef($i) if !defined $v;
    
    foreach(@_){
        my $k = $_->[1];
        $k = $k->($self) if ref $k;
        if($_->[0] eq '.'){
            if((my $ar = $_->[2]) || (ref $v || $v)->can($k)){
                $ar = $ar->($self) if ref($ar) eq 'CODE';
                $v = $v->$k($ar ? @$ar : ());
                $i .= ".$k".($ar ? (@$ar ? '(...)' : '()') : '') if defined $i;
            }else{
                $v = UNIVERSAL::isa($v, 'HASH') ? $v->{$k} : $self->_var_not_as($i, 'HASH');
                $i .= ".$k" if defined $i;
            }
        }elsif($_->[0] eq '['){
            $v = UNIVERSAL::isa($v, 'HASH') ? $v->{$k} : UNIVERSAL::isa($v, 'ARRAY') ? (
                $self->{warnings} ? $v->[$k] : do{ no warnings; $v->[$k] }
            ) : $self->_var_not_as($i, 'HASH or ARRAY');
            $i .= "[$k]" if defined $i;
        }else{
            die $self->_dump_($_);
        }
        return $self->_var_undef($i) if !defined $v;
    }
    $v;
}

sub _var_not_as {
    die qq{Can't use "$_[1]" as a $_[2] ref} if defined $_[1] && $_[0]->{strict};
    undef;
}

sub _var_undef {
    warn qq{Use of undefined value: "$_[1]"} if defined $_[1] && $_[0]->{warnings};
    '';
}

1;
